/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "generators/generators.h"

#include <vector>

namespace NAMESPACE{

// ****** Abstract generator implementation ********************************

const memory_size_t AbstractGenerator::DEFAULT_MAX_MEM_SIZE = 20000000; // byte

//  _____________________________________________________
// |   Protected functions                               |
// |_____________________________________________________|

void AbstractGenerator::free_buffer(std::vector<IOElement*>* buffer){
    if(!is_a_copy){
        std::vector<IOElement*>::iterator buff_it = buffer->begin();
        for(; buff_it != buffer->end(); buff_it++){
            IOElement* el = *buff_it;
            free_element(el);
            delete el;
        }
    }

    buffer->clear();
}

void AbstractGenerator::free_buffer(){
    free_buffer(generated_buffer);
}

// .......................................................


//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

AbstractGenerator::~AbstractGenerator(){
    delete generated_buffer;
    delete key;
}
AbstractGenerator::AbstractGenerator() : Generator(),
                                         generated_buffer(new std::vector<IOElement*>()),
                                         key(new IOKey()),
                                         is_a_copy(false),
                                         max_buffer_memory(DEFAULT_MAX_MEM_SIZE){}

/**
 * It makes a copy of each element pointed in the buffer by calling their copy constructors.
 * @param ag the object to copy
 */
AbstractGenerator::AbstractGenerator(const AbstractGenerator& ag) : Generator(ag),
                                                                    generated_buffer(new std::vector<IOElement*>(*(ag.generated_buffer))),
                                                                    key(new IOKey(*(ag.key))),
                                                                    is_a_copy(true),
                                                                    max_buffer_memory(ag.max_buffer_memory){}

AbstractGenerator& AbstractGenerator::operator=(const AbstractGenerator& ag){
    if(&ag == this) return *this;
    is_a_copy = *this != ag; // it is a copy only if at the current state it differs
                             // from the passed ag, as if they are equal, than you
                             // are trying to overwrite the original object with its own copy

    IOKey* tmp_key = key;
    std::vector<IOElement*>* tmp_buff = generated_buffer;    
    
    key = 0;
    generated_buffer = 0;
    try{
        generated_buffer = new std::vector<IOElement*>(*(ag.generated_buffer));
        key = new IOKey(*(ag.key));
    }catch(...){

        if(generated_buffer) delete generated_buffer;
        generated_buffer = tmp_buff;
        
        key = tmp_key;

        throw;
    }

    delete tmp_key;
    delete tmp_buff;

    max_buffer_memory = ag.max_buffer_memory;

    return *this;
}
// ......................................................

//  _____________________________________________________
// |   Other constructors                                |
// |_____________________________________________________|

AbstractGenerator::AbstractGenerator(const memory_size_t& _max_mem) : Generator(),
                                                                      generated_buffer(new std::vector<IOElement*>()),
                                                                      key(new IOKey()),
                                                                      is_a_copy(false),
                                                                      max_buffer_memory(_max_mem){}

AbstractGenerator::AbstractGenerator(const IOKey& k, 
                                     const memory_size_t& _max_mem) : Generator(),
                                                                      generated_buffer(new std::vector<IOElement*>()),
                                                                      key(new IOKey(k)),
                                                                      is_a_copy(false),
                                                                      max_buffer_memory(_max_mem){}

// ......................................................

//  _____________________________________________________
// |   Setters                                           |
// |_____________________________________________________|

Generator& AbstractGenerator::set_key(const IOKey& k){
    *key = k;
    return *this;
}

Generator& AbstractGenerator::set_key(const std::string& ns, const std::string& key_name){
    key->set_key(ns, key_name);
    return *this;
}

Generator& AbstractGenerator::set_max_buffer_mem(const memory_size_t& max_mem){
    max_buffer_memory = max_mem;
    return *this;
}
// ......................................................

//  _____________________________________________________
// |   Getters                                           |
// |_____________________________________________________|

const IOKey& AbstractGenerator::get_key() const{
    return *key;
}

const memory_size_t& AbstractGenerator::get_max_buffer_mem() const{
    return max_buffer_memory;
}

const std::vector<IOElement*>& AbstractGenerator::get_buffer() const{
    return *generated_buffer;
}
// ......................................................

IOElement* AbstractGenerator::last_generated() const{
    if(generated_buffer->size()) return *(generated_buffer->rbegin());
    return 0;
}

IOElement* AbstractGenerator::generate(void){
    // ****** Measure memory employment and free if necessary ******************
    memory_size_t mem_size = measure_memory();
    if(mem_size > max_buffer_memory){
        IOElement* el = *(generated_buffer->begin());
        free_element(el);
        delete el;
        generated_buffer->erase(generated_buffer->begin());
    }
    // -------------------------------------------------------------------------

    IOElement* element = generate(*key);
    generated_buffer->push_back(element);

    return element;
}

bool AbstractGenerator::operator==(const Generator& g) const{
    const AbstractGenerator* ag = dynamic_cast<const AbstractGenerator*>(&g);
    if(!ag) return false;
    if(*key != *(ag->key)) return false;
    if(generated_buffer->size() != ag->generated_buffer->size()) return false;

    std::vector<IOElement*>::const_iterator it = generated_buffer->begin();
    std::vector<IOElement*>::const_iterator ag_it = ag->generated_buffer->begin();
    for(; it != generated_buffer->end(); it++, ag_it++) if(**it != **ag_it) return false;

    return max_buffer_memory == ag->max_buffer_memory;
}

bool AbstractGenerator::operator!=(const Generator& g) const{
    return !AbstractGenerator::operator==(g);
}
// END NAMESPACE ---------------------------------------------------------------
}
